use axum::{
    Router,
    extract::{Path, State},
    http::StatusCode,
    response::Json,
    routing::{delete, get, post, put},
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, postgres::PgPool};
use std::sync::Arc;
use tower::ServiceBuilder;
use tower_http::cors::CorsLayer;
use uuid::Uuid;

#[derive(Debug, Serialize, Deserialize, FromRow)]
struct User {
    id: Uuid,
    name: String,
    email: String,
    age: Option<i32>,
    active: bool,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

#[derive(Debug, Deserialize)]
struct CreateUserRequest {
    name: String,
    email: String,
    age: Option<i32>,
}

#[derive(Debug, Deserialize)]
struct UpdateUserRequest {
    name: Option<String>,
    email: Option<String>,
    age: Option<i32>,
}

#[derive(Serialize)]
struct ApiResponse<T> {
    success: bool,
    data: Option<T>,
    message: String,
}

type DatabasePool = Arc<PgPool>;

impl<T> ApiResponse<T> {
    fn success(data: T, message: &str) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: message.to_string(),
        }
    }

    fn error(message: &str) -> Self {
        Self {
            success: false,
            data: None,
            message: message.to_string(),
        }
    }
}

async fn health() -> Json<ApiResponse<String>> {
    Json(ApiResponse::success("OK".to_string(), "Server is healthy"))
}

async fn get_users(
    State(pool): State<DatabasePool>,
) -> Result<Json<ApiResponse<Vec<User>>>, StatusCode> {
    let users = sqlx::query_as!(User, "SELECT * FROM users ORDER BY created_at DESC")
        .fetch_all(pool.as_ref())
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(Json(ApiResponse::success(
        users,
        "Users retrieved successfully",
    )))
}

async fn get_user(
    Path(user_id): Path<Uuid>,
    State(pool): State<DatabasePool>,
) -> Result<Json<ApiResponse<User>>, StatusCode> {
    let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", &user_id)
        .fetch_one(pool.as_ref())
        .await
        .map_err(|_| StatusCode::NOT_FOUND)?;

    Ok(Json(ApiResponse::success(user, "User found")))
}

///说明：SQLx 支持直接传递 Option<T> 类型作为参数，因此无需手动解包或加引用。原写法 &payload.age 导致类型变为 &Option<i32>，与数据库字段所需 i32 不符。去掉引用即可解决问题。
async fn create_user(
    State(pool): State<DatabasePool>,
    Json(payload): Json<CreateUserRequest>,
) -> Result<Json<ApiResponse<User>>, StatusCode> {
    let id = Uuid::new_v4();
    let now = Utc::now();

    let user = sqlx::query_as!(
        User,
        "INSERT INTO users (id, name, email, age, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *",
        &id,
        &payload.name,
        &payload.email,
        payload.age,
        &now,
        &now
    )
    .fetch_one(pool.as_ref())
    .await
    .map_err(|e| {
        println!("Database error: {}", e);
        StatusCode::INTERNAL_SERVER_ERROR
    })?;

    Ok(Json(ApiResponse::success(
        user,
        "User created successfully",
    )))
}

async fn update_user(
    Path(user_id): Path<Uuid>,
    State(pool): State<DatabasePool>,
    Json(payload): Json<UpdateUserRequest>,
) -> Result<Json<ApiResponse<User>>, StatusCode> {
    let now = Utc::now();

    // 先检查用户是否存在
    let existing_user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", &user_id)
        .fetch_one(pool.as_ref())
        .await
        .map_err(|_| StatusCode::NOT_FOUND)?;

    let name = payload.name.unwrap_or(existing_user.name);
    let email = payload.email.unwrap_or(existing_user.email);
    let age = payload.age.or(existing_user.age);

    let updated_user = sqlx::query_as!(
        User,
        "UPDATE users SET name = $1, email = $2, age = $3, updated_at = $4 WHERE id = $5 RETURNING *",
        &name,
        &email,
        age,
        &now,
        &user_id
    )
    .fetch_one(pool.as_ref())
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(Json(ApiResponse::success(
        updated_user,
        "User updated successfully",
    )))
}

async fn delete_user(
    Path(user_id): Path<Uuid>,
    State(pool): State<DatabasePool>,
) -> Result<Json<ApiResponse<String>>, StatusCode> {
    let result = sqlx::query!("DELETE FROM users WHERE id = $1", &user_id)
        .execute(pool.as_ref())
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    if result.rows_affected() == 0 {
        return Err(StatusCode::NOT_FOUND);
    }

    Ok(Json(ApiResponse::success(
        user_id.to_string(),
        "User deleted successfully",
    )))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 获取项目根目录路径
    // let project_root = std::env::current_dir()?.join("axum-sqlx");
    // let env_path = project_root.join(".env");

    // println!("项目根目录: {:?}", project_root);
    // println!("是否存在 .env 文件: {}", env_path.exists());

    // // 从项目根目录加载 .env 文件
    // if let Err(e) = dotenvy::from_path(&env_path) {
    //     println!("警告：无法加载 .env 文件，将使用环境变量或默认值: {}", e);
    // }
    // 创建数据库连接池
    let database_url = match std::env::var("DATABASE_URL") {
        Ok(url) => url,
        Err(std::env::VarError::NotPresent) => {
            println!("DATABASE_URL 环境变量未设置，使用默认值");
            "postgresql://postgres:123456@localhost:5432/postgres".to_string()
        }
        Err(e) => {
            println!("读取 DATABASE_URL 环境变量出错: {}", e);
            "postgresql://postgres:123456@localhost:5432/postgres".to_string()
        }
    };
    let pool = PgPool::connect(&database_url).await?;
    let pool = Arc::new(pool);

    // 构建应用路由
    let app = Router::new()
        .route("/health", get(health))
        .route("/users", get(get_users))
        .route("/users", post(create_user))
        .route("/users/{id}", get(get_user))
        .route("/users/{id}", put(update_user))
        .route("/users/{id}", delete(delete_user))
        .layer(ServiceBuilder::new().layer(CorsLayer::permissive()))
        .with_state(pool);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3001")
        .await
        .expect("Failed to bind to port 3001");

    println!("📋 Available endpoints:");
    println!("  GET    /health         - Health check");
    println!("  GET    /users          - Get all users");
    println!("  POST   /users          - Create new user");
    println!("  GET    /users/{{id}}     - Get user by ID");
    println!("  PUT    /users/{{id}}     - Update user by ID");
    println!("  DELETE /users/{{id}}     - Delete user by ID");
    println!("📄 Database: PostgreSQL ({})", database_url);

    println!("🚀 SQLx + Axum Server running on http://localhost:3001");

    axum::serve(listener, app)
        .await
        .expect("Failed to start server");

    Ok(())
}
